require 'uri'
require 'cgi'

class Didily < ActiveRecord::Base
    
    validates_presence_of :to_title, :message => "The destination uri title is required"
    validates_presence_of :to_uri, :message => "The destination uri cannot be empty"
    
    validates_presence_of :from_title, :message => "The source uri title is required"
    validates_presence_of :from_uri, :message => "The source uri cannot be empty"
    
    belongs_to :user
    belongs_to :relationship
    belongs_to :from_link, :foreign_key => "from_id", :class_name => "Link"
    belongs_to :to_link, :foreign_key => "to_id",   :class_name => "Link"
        
    
    ##########################################################
    # Gets all the didilies that match the given relationship id
    def self.Find_Didilies_By_Relation(id, limit = 20)
        sql = 'SELECT didilies.*, users.login, relationships.name, from_links.uri, to_links.uri FROM didilies ' 
        sql << 'INNER JOIN users ON users.id = didilies.user_id '
        sql << 'INNER JOIN relationships ON relationships.id = didilies.relationship_id '
        sql << 'INNER JOIN links from_links ON from_links.id = didilies.from_id '
        sql << 'INNER JOIN links to_links ON to_links.id = didilies.to_id '
        sql << 'WHERE ((relationships.id = ? )) ' 
        sql << 'ORDER BY didilies.created_at DESC '
        sql << 'LIMIT ' + limit.to_s
       
        self.find_by_sql([sql, id])
    end
    
    ###############################################
    # virtual attribute for the source uri.
    def from_uri
        if self.from_link
            return self.from_link.uri
        elsif @from_uri_value
            return @from_uri_value
        else
            return nil
        end
    end
    
    def from_uri=(new_uri)
        @from_uri_errors = Array.new
        @from_uri_value = format_uri(@from_uri_errors, new_uri, 'source')
        link = Link.find_or_create_by_uri(@from_uri_value)
        if !process_errors(link, @from_uri_errors)
            self.from_link = link 
        end
    end
    
    def from_title=(new_title)
        self[:from_title] = CGI::escapeHTML(new_title)
    end
    
    ###############################################   
    # virtual attribute for the destination uri.  
    def to_uri
        if self.to_link
            return self.to_link.uri
        elsif @to_uri_value
            return @to_uri_value
        else
            return nil
        end
    end
    
    def to_uri=(new_uri)
        @to_uri_errors = Array.new
        @to_uri_value = format_uri(@to_uri_errors, new_uri, 'destination')
        link = Link.find_or_create_by_uri(@to_uri_value)
        if !process_errors(link, @to_uri_errors)
            self.to_link = link 
        end     
    end
    
    def to_title=(new_title)
        self[:to_title] = CGI::escapeHTML(new_title)
    end
    
    ###############################################
    # virtual attribute for the description of the relationship
    def relation
        if self.relationship
            return self.relationship.name
        else
            return nil
        end
    end
    
    def relation=(name)
        self.relationship = Relationship.find_or_create_by_name(CGI::escapeHTML(name))
    end
    
    ##########################################################
    # search for associations
    def self.search(terms)
        sql = ''
        if terms.length > 0
            
            # add wild cards to the terms so the user doesn't have to get an exact match
            terms = terms.collect {|term| "%" + sanitize_sql(term) + "%"}
            
            # generate sql for terms in where clause
            terms_sql = " '" + terms.join("' AND text like '") +  "'"
            sql << " WHERE text LIKE " + terms_sql
            
            self.find(:all, 
                      :include=>[:user,:relationship,:from_link,:to_link],
                      :conditions => sql)
        end
    end
    
    protected
    
    ###############################################
    # formats and validates a uri
    def format_uri(error_array, new_uri, type)
        begin
            if new_uri.nil? || new_uri.length <= 0
                error_array.push('Please enter a value for the ' + type + ' url.')
            else
                uri = URI.parse(new_uri)
                if uri.scheme.nil?
                    new_uri = "http://" + new_uri
                    uri = URI.parse(new_uri)
                end                
                
                if uri.class != URI::HTTP && uri.class != URI::HTTPS && uri.class != URI::FTP
                    error_array.push('Please use a valid url for the ' + type + ' url.  i.e. http://www.example.com.  Also, Please be sure to include the "http".')
                end
            end
        rescue URI::InvalidURIError
            uri = new_uri # just pass the uri through.  Better to let a few bad uris through than to frustrate users - 
                          # the double lines in OCW get into send2wiki which are then sent to here.  According to 
                          # the URI.parse they are invalid, but they do work
            #error_array.push('The format of the ' + type + ' url is not valid.')
        end
        uri.to_s     
    end
    
    ###############################################
    # Adds errors from the model into the current
    # objects errors.  Returns false if no errors were
    # found.  True if errors were found.
    def process_errors(record, error_array)
        if record.errors.length > 0
            record.errors.each do |error_key, error_msg|
                error_array.push(error_msg)
            end
            return true
        else
            return false
        end
    end
    
    ###############################################
    # validation method called when model is saved
    def validate
        if !@from_uri_errors.nil? and @from_uri_errors.length > 0
            @from_uri_errors.each do |error|
                errors.add('Source URL:', error)
            end
        end
        if !@to_uri_errors.nil? and @to_uri_errors.length > 0
            @to_uri_errors.each do |error|
                errors.add('Destination URL:', error)
            end
        end        
    end
end